package tech.lq0.dreamaticvoyage.tools;

import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.*;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.texture.MissingTextureAtlasSprite;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.inventory.InventoryMenu;
import net.minecraftforge.client.extensions.common.IClientFluidTypeExtensions;
import net.minecraftforge.fluids.FluidStack;
import org.joml.Matrix4f;

public class RenderTool {

    public static void preciseBlit(GuiGraphics gui, ResourceLocation pAtlasLocation, float x, float y, float width, float height, float pBlitOffset, float pUOffset, float pVOffset, float uWidth, float vHeight, float pTextureWidth, float pTextureHeight) {
        float x2 = x + width, y2 = y + height;

        float pMinU = (pUOffset + 0.0F) / pTextureWidth, pMinV = (pVOffset + 0.0F) / pTextureHeight;
        float pMaxU = (pUOffset + uWidth) / pTextureWidth, pMaxV = (pVOffset + vHeight) / pTextureHeight;

        RenderSystem.setShaderTexture(0, pAtlasLocation);
        RenderSystem.setShader(GameRenderer::getPositionTexShader);
        Matrix4f matrix4f = gui.pose().last().pose();
        BufferBuilder bufferbuilder = Tesselator.getInstance().getBuilder();
        bufferbuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_TEX);
        bufferbuilder.vertex(matrix4f, x, y, pBlitOffset).uv(pMinU, pMinV).endVertex();
        bufferbuilder.vertex(matrix4f, x, y2, pBlitOffset).uv(pMinU, pMaxV).endVertex();
        bufferbuilder.vertex(matrix4f, x2, y2, pBlitOffset).uv(pMaxU, pMaxV).endVertex();
        bufferbuilder.vertex(matrix4f, x2, y, pBlitOffset).uv(pMaxU, pMinV).endVertex();
        BufferUploader.drawWithShader(bufferbuilder.end());
    }

    public static void preciseBlit(GuiGraphics gui, ResourceLocation pAtlasLocation, float x, float y, float width, float height, float pUOffset, float pVOffset, float uWidth, float vHeight, float pTextureWidth, float pTextureHeight) {
        preciseBlit(gui, pAtlasLocation, x, y, width, height, 0, pUOffset, pVOffset, uWidth, vHeight, pTextureWidth, pTextureHeight);
    }

    public static void preciseBlit(GuiGraphics gui, ResourceLocation pAtlasLocation, float x, float y, float pUOffset, float pVOffset, float width, float height, float pTextureWidth, float pTextureHeight) {
        preciseBlit(gui, pAtlasLocation, x, y, width, height, 0, pUOffset, pVOffset, width, height, pTextureWidth, pTextureHeight);
    }

    public static void preciseBlit(GuiGraphics gui, float pX, float pY, float pBlitOffset, float pWidth, float pHeight, TextureAtlasSprite pSprite, float pRed, float pGreen, float pBlue, float pAlpha) {
        preciseBlit(gui, pSprite.atlasLocation(), pX, pX + pWidth, pY, pY + pHeight, pBlitOffset, pSprite.getU0(), pSprite.getU1(), pSprite.getV0(), pSprite.getV1(), pRed, pGreen, pBlue, pAlpha);
    }

    /**
     * Performs the inner blit operation for rendering a texture with the specified coordinates, texture coordinates, and
     * color tint.
     *
     * @param pAtlasLocation the location of the texture atlas.
     * @param pX1            the x-coordinate of the first corner of the blit position.
     * @param pX2            the x-coordinate of the second corner of the blit position.
     * @param pY1            the y-coordinate of the first corner of the blit position.
     * @param pY2            the y-coordinate of the second corner of the blit position.
     * @param pBlitOffset    the z-level offset for rendering order.
     * @param pMinU          the minimum horizontal texture coordinate.
     * @param pMaxU          the maximum horizontal texture coordinate.
     * @param pMinV          the minimum vertical texture coordinate.
     * @param pMaxV          the maximum vertical texture coordinate.
     * @param pRed           the red component of the color tint.
     * @param pGreen         the green component of the color tint.
     * @param pBlue          the blue component of the color tint.
     * @param pAlpha         the alpha component of the color tint.
     */
    public static void preciseBlit(GuiGraphics gui, ResourceLocation pAtlasLocation, float pX1, float pX2, float pY1, float pY2, float pBlitOffset, float pMinU, float pMaxU, float pMinV, float pMaxV, float pRed, float pGreen, float pBlue, float pAlpha) {
        RenderSystem.setShaderTexture(0, pAtlasLocation);
        RenderSystem.setShader(GameRenderer::getPositionColorTexShader);
        RenderSystem.enableBlend();
        Matrix4f matrix4f = gui.pose().last().pose();
        BufferBuilder bufferbuilder = Tesselator.getInstance().getBuilder();
        bufferbuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR_TEX);
        bufferbuilder.vertex(matrix4f, pX1, pY1, pBlitOffset).color(pRed, pGreen, pBlue, pAlpha).uv(pMinU, pMinV).endVertex();
        bufferbuilder.vertex(matrix4f, pX1, pY2, pBlitOffset).color(pRed, pGreen, pBlue, pAlpha).uv(pMinU, pMaxV).endVertex();
        bufferbuilder.vertex(matrix4f, pX2, pY2, pBlitOffset).color(pRed, pGreen, pBlue, pAlpha).uv(pMaxU, pMaxV).endVertex();
        bufferbuilder.vertex(matrix4f, pX2, pY1, pBlitOffset).color(pRed, pGreen, pBlue, pAlpha).uv(pMaxU, pMinV).endVertex();
        BufferUploader.drawWithShader(bufferbuilder.end());
        RenderSystem.disableBlend();
    }

    /**
     * Codes based on @Tiviacz1337's Travelers-Backpack
     */
    public static void renderFluid(GuiGraphics guiGraphics, FluidStack fluid, int capacity, int amount, float x, float y, int offset, float height, float width) {
        if (fluid == null || fluid.isEmpty() || amount <= 0) return;

        var type = fluid.getFluid().getFluidType();
        TextureAtlasSprite atlasSprite = Minecraft.getInstance().getTextureAtlas(InventoryMenu.BLOCK_ATLAS)
                .apply(IClientFluidTypeExtensions.of(type).getStillTexture());
        if (atlasSprite == null) {
            atlasSprite = Minecraft.getInstance().getTextureAtlas(InventoryMenu.BLOCK_ATLAS).apply(MissingTextureAtlasSprite.getLocation());
        }

        int renderAmount = (int) Math.max(Math.min(height, amount * height / (float) capacity), 1);
        int posY = (int) (y + height - renderAmount);
        int color = IClientFluidTypeExtensions.of(type).getTintColor(fluid);

        guiGraphics.pose().pushPose();

        RenderSystem.setShader(GameRenderer::getPositionTexShader);
        RenderSystem.setShaderColor((color >> 16 & 0xFF) / 255F, (color >> 8 & 0xFF) / 255F, (color & 0xFF) / 255F, 1.0F);
        RenderSystem.setShaderTexture(0, InventoryMenu.BLOCK_ATLAS);
        RenderSystem.disableBlend();

        for (int i = 0; i < width; i += 16) {
            for (int j = 0; j < renderAmount; j += 16) {
                int w = (int) Math.min(width - i, 16);
                int h = Math.min(renderAmount - j, 16);

                int pX = (int) (x + i);
                int pY = posY + j;

                float minU = atlasSprite.getU0();
                float minV = atlasSprite.getV0();

                float maxU = atlasSprite.getU1();
                float maxV = atlasSprite.getV1();

                var matrix4f = guiGraphics.pose().last().pose();
                var builder = Tesselator.getInstance().getBuilder();
                builder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_TEX);
                builder.vertex(matrix4f, pX, pY + h, offset).uv(minU, minV + (maxV - minV) * (float) h / 16F).endVertex();
                builder.vertex(matrix4f, pX + w, pY + h, offset).uv(minU + (maxU - minU) * (float) w / 16F, minV + (maxV - minV) * h / 16F).endVertex();
                builder.vertex(matrix4f, pX + w, pY, offset).uv(minU + (maxU - minU) * w / 16F, minV).endVertex();
                builder.vertex(matrix4f, pX, pY, offset).uv(minU, minV).endVertex();
                BufferUploader.drawWithShader(builder.end());
            }
        }

        RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
        guiGraphics.pose().popPose();
    }
}
